/* Janus Chat - Version 1.0
 * Authors: Matthew Perry
 *          Michael Freid
 *          Marc Latou
 *          Richard Lee
 *
 * The Janus Chat Client is a highly secured
 * peer-to-peer chat client designed to facilitate
 * the transfer of sensitive data or as a communication
 * medium for the extra paranoid
 *
 * This file contains the implementation for the DetailTab
 * QT class which is an abstraction to factor out some
 * common functionality from the tab widgets which
 * facilitate the entering of information */

#include "detailtab.h"
#include "connecttab.h"
#include "lookupdialog.h"
#include <QMessageBox>
#include <QEvent>
#include <QKeyEvent>

//Low-level socket API
#ifndef _WIN32
    #include <sys/socket.h>
    #include <netinet/tcp.h>
#endif
#ifdef _WIN32
    #include <windows.h>
    #include <mstcpip.h>
#endif

/* The DetailTab constructor:
 * Simple constructor which initializes some
 * member variables */
DetailTab::DetailTab(User* user, QWidget *parent) :
    QWidget(parent)
{
    //Initialize my User
    myUser = user;

    //Initialize the private variable myTabWidget to this class' parent
    myTabWidget = (QTabWidget*)parent;

    //We need a new socket to connect with
    mySock = new QTcpSocket();
    //Connect mySock's signals to this class's slots
    connect( mySock, SIGNAL(connected()), this, SLOT(connectionEstablished()) );
    connect( mySock, SIGNAL(error(QAbstractSocket::SocketError)),
             this, SLOT(socketError(QAbstractSocket::SocketError)) );
}

/* The connectionEstablished slot:
 * This slot is activated when the socket object
 * emits the connect signal telling the class that
 * a successful connection has been made. This function
 * spawns a new chatTab with the socket */
void DetailTab::connectionEstablished()
{
    //Turn on Keep Alives
    mySock->setSocketOption( QAbstractSocket::KeepAliveOption, 1 );
    int sockFD = mySock->socketDescriptor();

    //Settings for Keep Alive Times
    int idle = 60, intvl = 5;
    //Low-level socket options for Keepalive timing
#ifndef _WIN32
    int count = 10;
    int optlen = sizeof( int );
    //Set the time before a keepalive is sent to 60 seconds
    setsockopt( sockFD, SOL_TCP, TCP_KEEPIDLE, &idle, optlen );
    //Set the time between keepalive packets to 5 seconds
    setsockopt( sockFD, SOL_TCP, TCP_KEEPINTVL, &intvl, optlen );
    //Set the number of packets before connection is closed to 5
    setsockopt( sockFD, SOL_TCP, TCP_KEEPCNT, &count, optlen );
#endif
#ifdef _WIN32
    //WinSock structure for KeepAlive timing settings
    struct tcp_keepalive settings;
    settings.onoff = 1;
    settings.keepalivetime = idle * 1000;
    settings.keepaliveinterval = intvl * 1000;

    //Random, useless pointers for WinSock call
    DWORD bytesReturned;
    WSAOVERLAPPED overlapped;
    overlapped.hEvent = NULL;
    //Set KeepAlive settings -- I HATE WINSOCK
    WSAIoctl(
        sockFD,
        SIO_KEEPALIVE_VALS,
        &settings,
        sizeof( struct tcp_keepalive ),
        NULL, 0,
        &bytesReturned,
        &overlapped,
        NULL
    );
#endif

    //Remove the signal slot relationship for our socket's errors
    disconnect(mySock, SIGNAL(error(QAbstractSocket::SocketError)),
               this, SLOT(socketError(QAbstractSocket::SocketError)) );
    //Function call to start chat
    beginChat();
}

/* The connectionCancelled slot:
 * This slot is activated by the lookupdialog's
 * cancelled signal. When the user hits cancel the
 * connect process halts. */
void DetailTab::connectionCancelled()
{
    mySock->abort();
}

/* The socketError slot:
 * This slot is activated by the socket object's
 * error() slot signifying that an error has occured
 * This function displays that error */
void DetailTab::socketError( QAbstractSocket::SocketError e )
{
    //Pop up message box with the errors
    QMessageBox msgBox;
    msgBox.setWindowTitle( "Socket Error!" );
    msgBox.setText( "An error occured while connecting:" );
    msgBox.setInformativeText( mySock->errorString() );
    msgBox.setIcon( QMessageBox::Critical );
    msgBox.exec();
}

/* The Ok Button Slot:
 * Activated when the OK button is clicked,
 * This slot tries to connect the socket object */
void DetailTab::buttonOk_clicked()
{
    QString errors = checkInput();
    if( errors.isEmpty() )
    {
        //Attempt to establish a connection by calling this virtual function
        connectSocket( mySock );
        //Create new LookupDialog to show user the progress
        LookupDialog ld( this->getAddress(), this );
        //Connect slots and signals for ld
        connect( mySock, SIGNAL(hostFound()), &ld, SLOT(foundHost()) );
        connect( mySock, SIGNAL(connected()), &ld, SLOT(connectionEstablished()) );
        connect( mySock, SIGNAL(error(QAbstractSocket::SocketError)),
                 &ld, SLOT(errorOccured(QAbstractSocket::SocketError)) );
        connect( &ld, SIGNAL(cancelled()), this, SLOT(connectionCancelled()) );
        //Spawn the ld
        ld.exec();
    }
    else
    {
        //Pop up message box with the errors
        QMessageBox msgBox;
        msgBox.setWindowTitle( "Input Error!" );
        msgBox.setText( "Errors have been found in your input:" );
        msgBox.setInformativeText( errors );
        msgBox.setIcon( QMessageBox::Critical );
        msgBox.exec();
    }
}

/* The Back Button Slot:
 * Activated when the Back button is clicked,
 * this function simply makes the current tab
 * an instance of connectTab */
void DetailTab::buttonBack_clicked()
{
    //Delete mySock
    delete mySock;
    //Initialize a new ConnectTab class
    ConnectTab* ct = new ConnectTab( myUser, myTabWidget );
    //Insert a new tab into the tabWidget using the ConnectTab widget because the Back button was clicked
    int n = myTabWidget->insertTab( myTabWidget->currentIndex(), ct, "Connect" );
    //Remove the old DirectTab because we have moved on
    myTabWidget->removeTab( myTabWidget->currentIndex() );
    //Emit the tabChanged signal to alert the tabWidget to readjust
    myTabWidget->setCurrentIndex( n );
}

/* The eventFilter:
 * For ease of use this function intercepts enter key presses
 * and redirects them to the OK button */
bool DetailTab::eventFilter( QObject* obj, QEvent* event )
{
    //Event must be a KeyPress
    if( event->type() == QEvent::KeyPress )
    {
        //Cast the generic event to a KeyPress
        QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
        //If the key pressed was the enter key
        if( keyEvent->key() == Qt::Key_Return )
        {
            //Press the OK button for the user
            this->buttonOk_clicked();
            return true;
        }
    }
    //Otherwise, act normally
    return QObject::eventFilter( obj, event );
}
